package com.contory.ssn_lgnd.app.maingame;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.Random;

import android.util.Log;

import com.contory.ssn_lgnd.app.Assets;
import com.contory.ssn_lgnd.app.comp.ControlerPtr;
import com.contory.ssn_lgnd.app.comp.DrawPtr;
import com.contory.ssn_lgnd.app.comp.UpdatePtr;
import com.contory.ssn_lgnd.app.comp.ViewObject;
import com.contory.ssn_lgnd.app.maingame.PlayerInfoGUI.WPSelector;
import com.contory.ssn_lgnd.framework.Pool.PoolObjectFactory;
import com.contory.ssn_lgnd.framework.gl.Camera2D;
import com.contory.ssn_lgnd.framework.gl.SpriteBatcher;
import com.contory.ssn_lgnd.framework.math.Circle;
import com.contory.ssn_lgnd.framework.math.OverlapTester;
import com.contory.ssn_lgnd.framework.math.Polygon;
import com.contory.ssn_lgnd.framework.math.PolygonResult;
import com.contory.ssn_lgnd.framework.math.Rectangle;
import com.contory.ssn_lgnd.framework.math.SpatialHashGrid;
import com.contory.ssn_lgnd.framework.math.Vector2;

public class Player implements DrawPtr, UpdatePtr, RangeViewObjectPtr, ColiObjPtr{
	public static final byte PLAYALBE = 0;
	public static final byte NONEPLAYABLE = 1;
	private static final float VIEW_WIDTH = 1f;
	private static final float VIEW_HEIGHT = 0.5f;
	public static final float MAX_VELOCITY_SCALA = 3.0f;
	private static final float ACCEL_SCALA = 3.0f;
	public static final int ID = 1000;
	ViewObject bodyView;
	ViewObject rangeView;
	public static float R = 3.0f;
	Vector2 velocity;
	Vector2 nDir;
	Vector2 tmpNdir;
	Vector2 incVel;
	
	Polygon coliBound;
	Circle rangeBound;
	PYellowBulletSocket yellowSocket;
	final Fire fire;
	final Camera2D cam;
	final Controler con;
	final ColisionManager cm;
	final ParticleEffect ptcEft;
	final ParticleEffectGroup ptcEftGroup;
	PlayerInfoGUI info = null;
	
	static final float COOLTIME_LIMIT = 1.0f;
	float coolTime = 0.0f;
	boolean fireEnalbeBool = true; 
	
	int fireType = -1;
	PolygonResultBuffer coliResultBuf;
	boolean enableBool = true;
	int energy = 100;
	byte state = 0;
	LinkedList<ColiObjPtr> targetBuffer;
	
	public Player(float strX, float strY, Camera2D cam, Controler con, ColisionManager cm, Fire fire, Random rand, ParticleEffectGroup ptcEftGroup){
		bodyView = new ViewObject();
		bodyView.setXY_WH_TR(strX, strY, VIEW_WIDTH, VIEW_HEIGHT, Assets.tex_reg_player);
		setViewDir(1);
		nDir = new Vector2();
		velocity = new Vector2();
		incVel = new Vector2();
		rangeView = new ViewObject();
		rangeView.setXY_WH_TR(bodyView.x, bodyView.y, R * 2, R * 2, Assets.tex2_reg_whiteTexel);
		rangeView.setRGBA(0, 1, 0, 0.5f);
		coliBound = new Polygon();
		coliBound.lockAddPoint(VIEW_WIDTH, VIEW_HEIGHT);
		coliBound.addPoint(0, 0);
		coliBound.addPoint(1, 0);
		coliBound.addPoint(1, 1);
		coliBound.addPoint(0, 1);
		coliBound.unLockAddPoint();
		coliBound.setPos(strX, strY);
		rangeBound = new Circle(bodyView.x, bodyView.y, R);
		
		yellowSocket = new PYellowBulletSocket(this);
		this.fire = fire;
		this.con = con;
		this.cam = cam;
		this.cm = cm;
		this.ptcEftGroup = ptcEftGroup;
		ptcEft = ptcEftGroup.addEffect();
		ptcEft.setInit();
		cm.addDynamicColiObj(this);
		
		enableBool = true;
		energy = 100;
		state = 0;
		
		tmpNdir = new Vector2();
		coliResultBuf = new PolygonResultBuffer();
		targetBuffer = new LinkedList<ColiObjPtr>();
	}
	
	public void setGUIInfo(PlayerInfoGUI info){
		this.info = info;
	}
	
	public void setPos(float x, float y){
		bodyView.x = x;
		bodyView.y = y;
	}
	
	public void setState(byte state){
		this.state = state;
	}
	
	@Override
	public void update(float deltaTime) {
		switch(state){
		case PLAYALBE:
			updateControler();
			updatePlayerInfo();
			updateBound();
			updateVelocity(deltaTime);
			updateColision(deltaTime);
			updateEvent();
			updateFire(deltaTime);
			updateView(deltaTime);
			updateParticle(deltaTime);
			//if Die : ptcEft.setDisable();
			break;
		
		case NONEPLAYABLE:
			nonePlayAbleUpdateView(deltaTime);
			updateParticle(deltaTime);
			break;
		}
	}
	
	private void updatePlayerInfo(){
		if(info == null){
			try {
				throw new Exception("Set PlayerInfo to Player");
			} catch (Exception e) {
			  Log.d("ASSERT", e.getMessage());
			}
		}
		fireType = info.getCurWP();
	}
	
	private void updateParticle(float deltaTime){
		if(!(velocity.x == 0 && velocity.y == 0)){
			if(bodyView.w > 0)
				ptcEft.makeParticle(-1, bodyView.x - VIEW_WIDTH / 2, bodyView.y);
			else
				ptcEft.makeParticle(1, bodyView.x + VIEW_WIDTH / 2, bodyView.y);
		}
	}
	
	private void updateControler(){
		nDir.set(con.getPointerNdir());
	}
	
	private void updateBound(){
		coliBound.setPos(bodyView.x, bodyView.y);
		rangeBound.center.set(bodyView.x, bodyView.y);
	}
	
	private void updateFire(float deltaTime){
		targetBuffer.clear();
		LinkedList<ColiObjPtr> coliDynaListPtr = cm.getDynamicObjList();
		for (ColiObjPtr coliDynaChdPtr : coliDynaListPtr) {
			if(coliDynaChdPtr.getID() == Enemy.ID){
				if(OverlapTester.polygonInCircle(this.rangeBound, coliDynaChdPtr.getColiBound())){
					targetBuffer.add(coliDynaChdPtr);
				}
			}
		}
		targetBufferSort();
		if(fireEnalbeBool){
			if(fireType == WPSelector.RED){
				if(targetBuffer.size() > 0)
					fire.fire(new PRedBullet(this.getPosX(), this.getPosY(), cm, targetBuffer.get(0), ptcEftGroup.addEffect()));
			}
			else if(fireType == WPSelector.YELLO){
				yellowSocket.fire(targetBuffer, 2, ptcEftGroup.addEffect());
			}
			fireEnalbeBool = false;
		}
		else{
			coolTime += deltaTime;
			if(coolTime > COOLTIME_LIMIT){
				fireEnalbeBool = true;
				coolTime = 0.0f;
			}
		}
	}
	
	Vector2 pTotVec = new Vector2();
	private void targetBufferSort(){
		if(targetBuffer.size() <= 1){
			return;
		}
		float[] len = new float[targetBuffer.size()];
		for(int i = 0; i < len.length; i++){
			pTotVec.set(targetBuffer.get(i).getPosX(), targetBuffer.get(i).getPosY());
			pTotVec.sub(bodyView.x, bodyView.y);
			len[i] = pTotVec.len();
		}
		for(int i = 1; i < len.length; i++){
			for(int k = i-1; k >= 0; k--){
				if(len[k+1] > len[k]){
					break;
				}
				float tmp = len[k+1];
				len[k+1] = len[k];
				len[k] = tmp;
				ColiObjPtr targetTmp = targetBuffer.get(k+1);
				targetBuffer.set(k+1, targetBuffer.get(k));
				targetBuffer.set(k, targetTmp);
			}
		}
		len = null;
	}
	
	private void updateEvent(){
		LinkedList<EventNotifier> eventObjListPtr = cm.getEventObjList();
		for (EventNotifier evenObjChdPtr : eventObjListPtr) {
			if(OverlapTester.overlapRectangles(this.coliBound.getBoundingBox(), 
					evenObjChdPtr.getColiBound().getBoundingBox())){
				evenObjChdPtr.excute();
			}
		}
	}
	
	private void updateColision(float deltaTime){
		PolygonResult resultPtr;
		LinkedList<ColiObjPtr> coliStaticListPtr = cm.getStaticObjList();
		for (ColiObjPtr coliStaticChdPtr : coliStaticListPtr) {
			if(coliStaticChdPtr.getID() == TerrainParts.ID){
				resultPtr = OverlapTester.overlapPolygons2(this.coliBound, coliStaticChdPtr.getColiBound(), incVel);
				if(resultPtr.willIntersectBool){
					coliResultBuf.add(new PolygonResult(resultPtr));
				}
			}
		}
	}
	
	private void updateVelocity(float deltaTime){
		if(nDir.x != 0 || nDir.y != 0){
			velocity.add(nDir.x * ACCEL_SCALA * deltaTime, nDir.y * ACCEL_SCALA * deltaTime);
			if(velocity.len() > MAX_VELOCITY_SCALA){
				velocity = velocity.nor();
				velocity.mul(MAX_VELOCITY_SCALA);
			}
		}
		else{
			tmpNdir.set(velocity);
			tmpNdir.nor();
			velocity.sub(tmpNdir.x * ACCEL_SCALA * deltaTime, tmpNdir.y * ACCEL_SCALA * deltaTime);
			if(tmpNdir.x > 0){
				if(velocity.x <= 0) 
					velocity.x = 0;
			}
			else if(tmpNdir.x < 0){
				if(velocity.x >= 0) 
					velocity.x = 0;
			}
			
			if(tmpNdir.y > 0){
				if(velocity.y <= 0) 
					velocity.y = 0;
			}
			else if(tmpNdir.y < 0){
				if(velocity.y >= 0) 
					velocity.y = 0;
			}
		}
		incVel.set(velocity.x  * deltaTime, velocity.y  * deltaTime);
	}
	
	private void nonePlayAbleUpdateView(float deltaTime){
		rangeView.x = bodyView.x; 
		rangeView.y = bodyView.y; 
	
		bodyView.x += deltaTime * velocity.x;
		bodyView.y += deltaTime * velocity.y;
		
		setViewDir(nDir.x);
	}
	
	private void updateView(float deltaTime){
		rangeView.x = bodyView.x; 
		rangeView.y = bodyView.y; 
		
		float thisLX = this.coliBound.getBoundingBox().lowerLeft.x;
		float thisW = this.coliBound.getBoundingBox().width;
		float camLX = cam.getBoundRect().lowerLeft.x;
		float camW = cam.getBoundRect().width;
		float thisLY = this.coliBound.getBoundingBox().lowerLeft.y;
		float thisH = this.coliBound.getBoundingBox().height;
		float camLY = cam.getBoundRect().lowerLeft.y;
		float camH = cam.getBoundRect().height;
		
		// World bound
		if(thisLX + incVel.x <= camLX){
			bodyView.x += Math.abs((camLX - (thisLX + incVel.x)));
		}
		if(thisLX + thisW + incVel.x >= camLX + camW){
			bodyView.x -= Math.abs((camLX + camW - (thisLX + thisW + incVel.x)));
		}
		if(thisLY + incVel.y <= camLY){
			bodyView.y += Math.abs((camLY - (thisLY + incVel.y)));
		}
		if(thisLY + thisH + incVel.y >= camLY + camH){
			bodyView.y -= Math.abs((camLY + camH - (thisLY + thisH + incVel.y)));
		}
		
		for(int i = 0; i < coliResultBuf.getSize(); i++){
			PolygonResult resultPtr = coliResultBuf.getResult(i);
			if(resultPtr.willIntersectBool){
				bodyView.x += resultPtr.minimumTlanslationVector.x;
				bodyView.y += resultPtr.minimumTlanslationVector.y;
			}
		}
		
		coliResultBuf.clear();
		bodyView.x += incVel.x;
		bodyView.y += incVel.y;
		
		if(nDir.x != 0 || nDir.y != 0){
			setViewDir(nDir.x);
		}
		else{
			bodyView.y -= deltaTime * 0.05f;
		}
	}
	
	public void setViewDir(float dirX){
		if(dirX >= 0){
			bodyView.w = VIEW_WIDTH;
		}
		else{
			bodyView.w = -VIEW_WIDTH;
		}
	}
	
	@Override
	public void draw(SpriteBatcher batcher) {
		batcher.drawSprite(bodyView.x, bodyView.y, bodyView.w, bodyView.h, bodyView.tr);
	}

	@Override
	public void rangeViewDraw(SpriteBatcher batcher) {
		batcher.drawSprite(rangeView.x, rangeView.y, rangeView.w, rangeView.h,
				rangeView.r, rangeView.g, rangeView.b, rangeView.a, rangeView.tr);
	}

	@Override
	public Polygon getColiBound() {
		return coliBound;
	}

	@Override
	public float getPosX() {
		return bodyView.x;
	}

	@Override
	public float getPosY() {
		return bodyView.y;
	}
	
	@Override
	public int getID() {
		return ID;
	}

	@Override
	public boolean getEnableBool() {
		return enableBool;
	}

	@Override
	public void setEnable(boolean enalbeBool) {
		this.enableBool = enalbeBool;
	}
	
	@Override
	public void setEnergy(int minusDelta) {
		this.energy += minusDelta;
		if(energy < 0){
			energy = 0;
		}
	}

	@Override
	public int getEnergy() {
		return energy;
	}
}
